import MetaTrader5 as mt5
import pandas as pd
from control.order_control import OrderControl, Order, OrderType
from utils.e_mail import sendMail
import datetime
from utils.log_util import prints, saveLog
from constant.plan_constant import trade_plan
from control.indicator_control import IndicatorControl
from utils.time_util import today_hour_stamp, current_time, local_2_gmt, datetime2Str, str2datetime, stamp2datetime
import constant.param_config as config
from constant.param_config import getMarketCloseAlert, changeMarketCloseAlert
from constant.globl_constant import strategyList, fibList, fibList2, fibList3


class BaseWorker:
    """
    账号信息
    """
    # r = socket.gethostbyname(socket.gethostname())

    TRADE_ACTION_DEAL = mt5.TRADE_ACTION_DEAL  # 以指定参数（设置市价单）下现价交易订单。
    TRADE_ACTION_PENDING = mt5.TRADE_ACTION_PENDING  # 在指定条件下（挂单）下执行交易订单
    TRADE_ACTION_SLTP = mt5.TRADE_ACTION_SLTP  # 更改持仓止损和止盈
    TRADE_ACTION_MODIFY = mt5.TRADE_ACTION_MODIFY  # 更改之前所下交易订单的参数
    TRADE_ACTION_REMOVE = mt5.TRADE_ACTION_REMOVE  # 删除之前所下的挂单
    TRADE_ACTION_CLOSE_BY = mt5.TRADE_ACTION_CLOSE_BY  # 反向平仓
    TRADE_RETCODE_DONE = mt5.TRADE_RETCODE_DONE

    ORDER_TYPE_BUY = mt5.ORDER_TYPE_BUY
    ORDER_TYPE_SELL = mt5.ORDER_TYPE_SELL
    ORDER_TYPE_BUY_LIMIT = mt5.ORDER_TYPE_BUY_LIMIT
    ORDER_TYPE_SELL_LIMIT = mt5.ORDER_TYPE_SELL_LIMIT
    ORDER_TYPE_BUY_STOP = mt5.ORDER_TYPE_BUY_STOP
    ORDER_TYPE_SELL_STOP = mt5.ORDER_TYPE_SELL_STOP
    ORDER_TYPE_BUY_STOP_LIMIT = mt5.ORDER_TYPE_BUY_STOP_LIMIT
    ORDER_TYPE_SELL_STOP_LIMIT = mt5.ORDER_TYPE_SELL_STOP_LIMIT
    ORDER_TYPE_CLOSE_BY = mt5.ORDER_TYPE_CLOSE_BY

    POSITION_TYPE_BUY = mt5.POSITION_TYPE_BUY
    POSITION_TYPE_SELL = mt5.POSITION_TYPE_SELL

    # 日内有重要事件时，等待数据公布后由事件服务进行交易
    forbidDeal = False
    # 重要数据是否公布
    eventShow = False
    mustLogin = True

    # 日内盈利
    dayProfitDict = {}

    lastNotice = 0

    def __init__(self):
        self.symble = config.getSymble()
        self.url = config.getUrl()
        self.goldPriceOffset = config.getPriceOffset()

        self.control = OrderControl()
        self.indicator = IndicatorControl()

        if self.mustLogin:
            self.isLogin = self.mqlLogin()

    def mqlLogin(self):
        if mt5.account_info() is None:
            if not mt5.initialize(path=config.getMt5Path()):
                print("登录错误：" + mt5.last_error()[1])
                saveLog("initialize() failed, error code =" +
                        mt5.last_error()[1])
                return False

            authorized = mt5.login(login=config.getMt5Account(),
                                   password=config.getMt5Password(),
                                   server=config.getMt5Server())
            if authorized:
                saveLog("connected to account #{}".format(
                    config.getMt5Account()))
                return True
            else:
                saveLog(
                    "failed to connect at account #{}, error code: {}".format(
                        config.getMt5Account(), mt5.last_error()))
                return False
        return True

    def dayPriceInfo(self, symbol):
        """
        获取日内开盘价和最高价
        """
        kValue = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 0, 1)
        frameValue = pd.DataFrame(kValue)
        highArr = frameValue["high"][0]
        lowArr = frameValue["low"][0]
        openArr = frameValue["open"][0]
        closeArr = frameValue["close"][0]
        return highArr, lowArr, openArr, closeArr

    def getCurrentInfo(self, symbol=""):
        """
        获取当前价格信息
        """
        return mt5.symbol_info(symbol)

    def getClientInfo(self):
        """
        获取客户端信息
        """
        return mt5.terminal_info()

    def getPositionsTotal(self):
        """
        获取当前持仓数量
        """
        return mt5.positions_total()

    def dayAllProfit(self):
        """
        日内共计盈利
        """
        dayAllProfit = 0
        historyList = self.dayAllOrder()
        positionList = self.getPositionsDetail(symbol=self.symble)

        for item in historyList:
            if "profit" in item.keys():
                dayAllProfit = dayAllProfit + item["profit"]

        for position in positionList:
            dayAllProfit = dayAllProfit + position.profit
        return dayAllProfit

    def getPlanState(self, logState=None, serviceName=""):
        """
        获取计划日内目标盈利
        """
        balance = self.getAccountMoney()
        targe = self.getPlanDayTarge()

        todayStamp = today_hour_stamp()  # 零点时间戳
        if todayStamp not in self.dayProfitDict:
            self.dayProfitDict.clear()
            self.dayProfitDict[todayStamp] = targe
        else:
            targe = self.dayProfitDict[todayStamp]

        if balance < targe:
            # 当前账户实时资金小于日内目标盈利金额，继续交易
            return True

        if logState is False:
            saveLog("日内目标盈利已完成，" + serviceName + "已暂停")
        return False

    def tradeState(self):
        """
        交易状态
        return True:继续交易   False:停止交易
        """
        if self.getPlanState() is False:  #达到目标盈利
            if config.getContinueTrade():  #继续交易
                return True
            return False
        return True

    def getPlanDayTarge(self):
        """
        计划目标金额
        """
        allProfit = self.dayAllProfit()
        balance = self.getAccountMoney()

        # 获取日内初始资金
        agoMoney = int((balance - allProfit) * 100) / 100
        lot, profit = self.getPlanLot(balance=agoMoney)
        if lot != None and profit != None:
            planDayTarge = lot * 100 * 1 + agoMoney
            return planDayTarge
        return balance + 2

    def getPlanLot(self, balance=None):
        """
        获取计划内的下单手数和目标点数
        """

        current = balance
        if balance == None:
            accountInfo = mt5.account_info()
            if accountInfo is None:
                return None, None
            current = accountInfo.balance

        lot = 0.01
        profit = 1

        for index in range(0, len(trade_plan)):
            value = trade_plan[index]
            lot = value["lot"]
            profit = value["profit"]
            planDollar = value["initDollar"]

            if index == 0 and current <= planDollar:
                break

            if index == len(trade_plan) - 1 and current >= planDollar:
                break

            nextDollar = trade_plan[index + 1]["initDollar"]
            if current >= planDollar and current <= nextDollar:
                break
        return lot, profit

    def getAccountMoney(self):
        """
        账户实时资金
        """
        return self.getAccountInfo().equity

    def getAccountInfo(self):
        return mt5.account_info()

    def dayAllOrder(self):
        """
        日内所有订单
        """
        nowTime = datetime.datetime.now()
        gmtDateTime = str2datetime(local_2_gmt(datetime2Str(nowTime)))
        diffTimeStamp = int(nowTime.timestamp()) - int(gmtDateTime.timestamp())

        startTimeStamp = int(
            datetime.datetime(gmtDateTime.year, gmtDateTime.month,
                              gmtDateTime.day, 0).timestamp()) + diffTimeStamp
        startTime = stamp2datetime(startTimeStamp)

        # gmtStart = local_2_gmt(datetime2Str(localStart))
        # gmtDateTime = str2datetime(gmtStart)
        allOrder = self.historyOrder(nowTime, startTime=startTime)

        self.listOrder = []
        self.changeOrer = []
        for item in allOrder:
            result = [
                child for child in self.listOrder
                if child["position_id"] == item.position_id
            ]

            if len(result) == 0:
                kMap = {}
                kMap["type"] = item.type
                kMap["volume"] = item.volume
                kMap["position_id"] = item.position_id
                self.listOrder.append(self.createMap(kMap, item))
            else:
                self.changeOrer.append(self.createMap(result[0], item))
        return self.changeOrer

    def getLastRechangeOrder(self):
        """
        获取上次充值到现在所有的订单
        """
        rechangeTime = config.getRechangeDate()
        # gmtDateTime = str2datetime(local_2_gmt(datetime2Str(rechangeTime)))
        allOrder = self.historyOrder(datetime.datetime.now(),
                                     startTime=rechangeTime)
        if allOrder is not None and len(allOrder) != 0:
            return [
                item for item in allOrder
                if item.entry == 1 and item.comment != "D-NULL" and "D-ALLINT"
                not in item.comment and "W-ALLINT" not in item.comment
            ]
        return []

    def getRechargeOrder(self):
        """
        获取所有充值订单
        """
        allOrder = self.historyOrder(datetime.datetime.now())
        if allOrder is not None and len(allOrder) != 0:
            return [
                item for item in allOrder if item.type == 2
                and item.comment != "D-NULL" and "D-ALLINT" in item.comment
            ]
        return []

    def historyOrder(self, endTime, startTime=0):
        """
        所有历史订单
        """
        return mt5.history_deals_get(startTime, endTime)

    def createMap(self, newMap, order):
        """
        创建k线对象
        """
        if order.entry == 0:
            newMap["start_time"] = order.time
            newMap["open_price"] = order.price
            newMap["comment"] = order.comment
        else:
            newMap["end_time"] = order.time
            newMap["close_price"] = order.price
            newMap["profit"] = order.profit
        return newMap

    def getClosePriceArray(self, symbol, timeframe, count=200):
        """
        获取收盘价
        """
        closeArr = []
        result = self.getKLine(symbol, count, timeframe, start=0)["value"]

        if result is None:
            return []

        for item in result:
            closeArr.append(item[4])
        return closeArr

    def getHighPriceArray(self, symbol, timeframe, count=200):
        """
        获取最高价
        """
        highArr = []
        result = self.getKLine(symbol, count, timeframe, start=0)["value"]
        for item in result:
            highArr.append(item[2])
        return highArr

    def getOrderForType(self, state):
        """
        获取多空订单
        """
        total = self.getPositionsDetail(symbol=self.symble)
        if total is None or len(total) == 0:
            return []

        if state == Order.CLOSE_BUY:
            return list({
                item
                for item in total if item.type == self.POSITION_TYPE_BUY
            })
        elif state == Order.CLOSE_SELL:
            return list({
                item
                for item in total if item.type == self.POSITION_TYPE_SELL
            })
        else:
            return []

    def getLowPriceArray(self, symbol, timeframe, count=200):
        """
        获取最低价
        """
        lowArr = []
        result = self.getKLine(symbol, count, timeframe, start=0)["value"]
        for item in result:
            lowArr.append(item[3])
        return lowArr

    def getKLine(self, symbol, count, timeFrams=None, start=1, reverse=False):
        """
        获取K线
        """
        lineMap = {}
        if timeFrams == None:
            lineMap["m1"] = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1,
                                                    start, count)
            lineMap["m5"] = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M5,
                                                    start, count)
            lineMap["m15"] = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M15,
                                                     start, count)
            lineMap["m30"] = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M30,
                                                     start, count)
            lineMap["h1"] = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_H1,
                                                    start, count)

            if reverse is True:
                lineMap["m1"] = lineMap["m1"][::-1]
                lineMap["m5"] = lineMap["m5"][::-1]
                lineMap["m15"] = lineMap["m15"][::-1]
                lineMap["m30"] = lineMap["m30"][::-1]
                lineMap["h1"] = lineMap["h1"][::-1]
        else:
            lineMap["value"] = mt5.copy_rates_from_pos(symbol, timeFrams,
                                                       start, count)
            if reverse is True:
                lineMap["value"] = lineMap["value"][::-1]
        return lineMap

    def getFibPriceList(self, symble, timeFrame, count):
        """
        获取斐波那契计算数组
        """
        priceArr = pd.DataFrame(
            mt5.copy_rates_from_pos(symble, timeFrame, 1, count)[::-1])
        closeList = self.getClosePrice(priceArr, "close")
        currentPrice = mt5.symbol_info_tick(symble).ask

        # 从范围收盘价找出最大最小值
        maxNum, maxIndex = max((v, i) for i, v in enumerate(closeList))
        minNum, minIndx = min((v, i) for i, v in enumerate(closeList))

        # 最大最小值距离当前价格位有一定距离
        fibOffset = config.getFibOffset()
        if maxNum - fibOffset > currentPrice and minNum + fibOffset < currentPrice:
            return closeList, priceArr, timeFrame
        else:
            if timeFrame == mt5.TIMEFRAME_M1:
                timeFrame = mt5.TIMEFRAME_M5
            elif timeFrame == mt5.TIMEFRAME_M5:
                timeFrame = mt5.TIMEFRAME_M15
            elif timeFrame == mt5.TIMEFRAME_M15:
                timeFrame = mt5.TIMEFRAME_M30
            elif timeFrame == mt5.TIMEFRAME_M30:
                timeFrame = mt5.TIMEFRAME_H1
            elif timeFrame == mt5.TIMEFRAME_H1:
                timeFrame = mt5.TIMEFRAME_H4
            elif timeFrame == mt5.TIMEFRAME_H4:
                timeFrame = mt5.TIMEFRAME_D1
            else:
                return closeList, priceArr, timeFrame
            return None, priceArr, timeFrame

    def getClosePrice(self, frame, keyValue, reverse=False):
        """
        转为收盘价数组
        """
        series = frame[keyValue]
        newList = series.to_list()
        if (reverse):
            newList.reverse()
        return newList

    def getPositionsDetail(self, symbol=None, group=None, ticket=None):
        """
        获取可通过交易品种或单号筛选的未结持仓
        symbol:     交易品种名称
        group:      用于选择交易品种订单的过滤器
        ticket:     单号
        """
        positions = []
        if symbol is not None:
            positions = mt5.positions_get(symbol=symbol)
        elif group is not None:
            positions = mt5.positions_get(group=group)
        elif ticket is not None:
            positions = mt5.positions_get(ticket=ticket)
        else:
            positions = mt5.positions_get()
        return [] if positions == None else positions

    def getLimitOrderDetail(self,
                            symbol=None,
                            ticket=None,
                            price=None,
                            t=None):
        """
        获取挂单详情
        """
        allOrder = [
            item for item in mt5.orders_get()
            if item.type == 2 or item.type == 3
        ]
        if len(allOrder) != 0 and symbol is not None:
            allOrder = [item for item in allOrder if item.symbol == symbol]

        if len(allOrder) != 0 and ticket is not None:
            allOrder = [item for item in allOrder if item.ticket == ticket]

        if len(allOrder) != 0 and price is not None:
            allOrder = [item for item in allOrder if item.price_open == price]

        if len(allOrder) != 0 and t is not None:
            allOrder = [item for item in allOrder if item.type == t]
        return allOrder

    def marketClose(self):
        """
        市场关闭
        """
        symbolInfo = mt5.symbol_info(self.symble)
        if symbolInfo is not None:
            cTime = current_time()
            time = cTime - symbolInfo.time
            if time >= config.getMarketCloseOffset():
                if getMarketCloseAlert() is False:
                    saveLog("市场已关闭")
                    changeMarketCloseAlert(True)
                return True
        changeMarketCloseAlert(False)
        self.changeTradeMode()
        return False

    def changeTradeMode(self):
        """
        修改下单参数
        """
        if config.tradeMode == config.TradeMode.planMode:
            lot, tpSpace = self.getPlanLot()
            if lot != None and config.getDefaultLots() != lot:
                config.changeDefaultLots(lot)

            if tpSpace != None and config.getDayTarger() != tpSpace:
                config.changeDayTarger(lot * 100 * tpSpace)

    def orderTimeOut(self, orderList):
        """
        订单超时处理
        """
        closeState = False
        for order in orderList:
            diffTime = config.holdOrderTime
            minProfit = order.volume * 100 * 0.1
            maxProfit = order.volume * 100 * 0.5
            if current_time(
            ) - order.time > diffTime and order.profit > minProfit and order.profit < maxProfit:
                closeState = True
                self.control.closeOrder(order)

        if closeState:
            saveLog("持仓时间大于限定时间，已进行平仓操作")
            sendMail("持仓时间大于限定时间，已进行平仓操作", timer=False)

    def closeOrder(self, order):
        self.control.closeOrder(order)

    def profitState(self):
        """
        盈利状态
        """
        dayLoss = config.getDayLoss()
        dayTarger = config.getDayTarger()
        profitCount = self.dayAllProfit()

        if profitCount > 0 and profitCount > dayTarger:
            if self.profitAlert is False:
                saveLog("日内目标收益已完成，停止交易")
                sendMail("日内目标收益已完成，停止交易")
                total = self.getPositionsDetail(symbol=self.symble)
                if total != None and len(total) != 0:
                    for item in total:
                        self.closeOrder(item)
            return False
        elif dayLoss != -1 and profitCount < 0 and abs(profitCount) > dayLoss:
            if self.profitAlert is False:
                saveLog("日内亏损已达限定值，停止交易")
                sendMail("日内亏损已达限定值，停止交易")
                total = self.getPositionsDetail(symbol=self.symble)
                if total != None and len(total) != 0:
                    for item in total:
                        self.closeOrder(item)
            return False
        return True

    def extent(self, printLog=True):
        """
        涨跌幅
        """
        symbolInfo = self.getCurrentInfo(self.symble)
        if symbolInfo is not None:
            openPrice = symbolInfo.session_open
            askPrice = symbolInfo.ask
            highPrice = symbolInfo.askhigh
            lowPrice = symbolInfo.bidlow

            value = abs((openPrice - askPrice) / openPrice * 100)
            down = abs((highPrice - askPrice) / highPrice * 100)
            up = abs((askPrice - lowPrice) / lowPrice * 100)

            currentPrice = self.control.getCurrentPrice(
                self.symble, orderType=Order.CREATE_BUY)

            noticeSpace = config.getNoticeSpace()
            extentValue = config.getExtentValue()
            if askPrice > openPrice:
                self.notice = "当前价格为" + str(currentPrice) + "，涨幅：" + format(
                    value, '.2f')
            else:
                self.notice = "当前价格为" + str(currentPrice) + "，跌幅：" + format(
                    value, '.2f')

            if self.allowTimeCheck() and (self.lastNotice == 0
                                          or current_time() - self.lastNotice
                                          > noticeSpace):
                sendMail(self.symble + self.notice)
                self.lastNotice = current_time()

            if value > extentValue:
                if printLog:
                    saveLog(self.notice + "，允许交易")
                return True
        if printLog:
            saveLog(self.notice + "，禁止交易")
        return False

    def allowTimeCheck(self):
        """
        通知时间判断
        """
        nowTime = datetime.datetime.now().hour
        startTime = config.getNoAllowStartTime()
        endTime = config.getNoAllowEndTime()
        if startTime > endTime and nowTime < startTime and nowTime > endTime:
            return True
        elif startTime < endTime and nowTime < startTime and nowTime > endTime:
            return True
        return False

    def maState(self, t, currentPrice):
        """
        30分钟周期内
        多单时必须20日线在30日线上方
        空单时必须20日线在30日线下方
        """
        closeArr = []
        result = self.getKLine(self.symble, 100, mt5.TIMEFRAME_M30)["value"]
        for item in result:
            closeArr.append(item[4])

        ma30 = self.indicator.latestMaValue(closeArr, 30)
        ma20 = self.indicator.latestMaValue(closeArr, 20)

        if t == Order.CREATE_BUY and ma20 > ma30 and currentPrice > ma20:
            return self.macdState(t, closeArr)
        elif t == Order.CREATE_SELL and ma30 > ma20 and ma20 > currentPrice:
            return self.macdState(t, closeArr)
        return False

    def macdState(self, t, currentPrice):
        """
        15分钟周期,价格跌破30分钟均线时,止损止盈
        且当前区间内禁止下单
        """

        closeArr = []
        result = self.getKLine(self.symble, 100, mt5.TIMEFRAME_M15)["value"]
        for item in result:
            closeArr.append(item[4])

        maValue = self.indicator.latestMaValue(closeArr, 30)

        latest = result[-1]
        # 前一根k线在30分钟均线下方，且是阴线，或者当前价格小于前一根K线最高价
        if ((maValue - self.goldPriceOffset / 2 > latest[1]
             and latest[1] > latest[4])
                or currentPrice < latest[3]) and t == Order.CREATE_SELL:
            return False
        # 前一根k线在30分钟均线上方，且是阳线，或者当前价格大于前一根K线最高价
        elif ((latest[1] > maValue + self.goldPriceOffset / 2
               and latest[4] > latest[1])
              or currentPrice > latest[2]) and t == Order.CREATE_BUY:
            return False
        return True

    def macdCheck(self, t, currentPrice):
        """
        macd阻力检查
        """

        maValueTime = [50, 100, 200]
        m15ClosePrice = self.getClosePriceArray(self.symble, mt5.TIMEFRAME_M15)
        m30ClosePrice = self.getClosePriceArray(self.symble, mt5.TIMEFRAME_M30)
        h1ClosePrice = self.getClosePriceArray(self.symble, mt5.TIMEFRAME_H1)
        h4ClosePrice = self.getClosePriceArray(self.symble, mt5.TIMEFRAME_H4)
        D1ClosePrice = self.getClosePriceArray(self.symble, mt5.TIMEFRAME_D1)

        m15SMA = self.indicator.maValueForTimeArray(closeArr=m15ClosePrice,
                                                    timeArray=maValueTime)
        m30SMA = self.indicator.maValueForTimeArray(closeArr=m30ClosePrice,
                                                    timeArray=maValueTime)
        h1SMA = self.indicator.maValueForTimeArray(closeArr=h1ClosePrice,
                                                   timeArray=maValueTime)
        h4SMA = self.indicator.maValueForTimeArray(closeArr=h4ClosePrice,
                                                   timeArray=maValueTime)
        D1SMA = self.indicator.maValueForTimeArray(closeArr=D1ClosePrice,
                                                   timeArray=maValueTime)

        maPrice = []
        smaPrice = m15SMA + m30SMA + h1SMA + h4SMA + D1SMA
        if t == Order.CREATE_BUY:
            maPrice = sorted(
                [item for item in smaPrice if item > currentPrice])
        elif t == Order.CREATE_SELL:
            maPrice = sorted(
                [item for item in smaPrice if item < currentPrice],
                reverse=True)

        for item in maPrice:
            if currentPrice < item + config.goldPriceOffset * 2 and currentPrice > item - config.goldPriceOffset * 2:
                return True
        return False

    def shortFrame(self):
        """
        一分钟周期均线值
        """

        closeArr = []
        result = self.getKLine(self.symble, 100, mt5.TIMEFRAME_M1)["value"]
        for item in result:
            closeArr.append(item[4])

        ma15Value = self.indicator.latestMaValue(closeArr, 15)
        ma30Value = self.indicator.latestMaValue(closeArr, 30)
        ma50Value = self.indicator.latestMaValue(closeArr, 50)
        ma100Value = self.indicator.latestMaValue(closeArr, 100)
        return [ma15Value, ma30Value, ma50Value, ma100Value]

    def createOrderCheck(self, logState, serviceName):
        """
        下单前检查
        """

        if self.getForbidState(logState, serviceName):
            # 日内有重要事件公布时，禁止交易
            return False

        if self.getPlanState(logState, serviceName) is False:
            # 达到日内目标盈利时，禁止交易
            return False
        else:
            return True

    def sameRemarkOrder(self, remark, reversal=False):
        """
        检查同类型订单
        """
        total = self.getPositionsDetail(symbol=self.symble)
        if len(total) != 0:
            orderList = []
            if reversal is False:
                orderList = [item for item in total if remark in item.comment]
            else:
                orderList = [
                    item for item in total if remark not in item.comment
                ]
            if len(orderList) != 0:
                return True
        return False

    def checkProfit(self, logState, serviceName):
        """
        日内收益检查
        """
        dayLoss = config.getDayLoss()
        dayTarger = config.getDayTarger()
        profitCount = self.dayAllProfit()

        if profitCount > 0 and profitCount > dayTarger:
            if logState is False:
                saveLog(serviceName + ":日内目标收益已完成，停止交易")
            return False
        elif dayLoss != -1 and profitCount < 0 and abs(profitCount) > dayLoss:
            if logState is False:
                saveLog(serviceName + ":日内亏损已达限定值，停止交易")
            return False
        return True

    def getForbidState(self, logState, serviceName):
        """
        重要事件检测
        """
        if BaseWorker.forbidDeal:
            if logState is False:
                saveLog("稍后有重要事件公布，" + serviceName + "已暂停")
            return True
        return False

    def forbid(self, t):
        """
        禁止下单
        """

        total = self.getPositionsDetail(symbol=self.symble)
        currentPrice = self.control.getCurrentPrice(self.symble, orderType=t)

        # MACD状态
        if self.macdState(t, currentPrice):
            saveLog("前一根k线不在30分钟均线上/下方, 禁止下单")
            return True

        # 斐波那契阻力位
        if self.fibFilter(currentPrice):
            saveLog("当前价格在斐波那契阻力位，禁止下单")
            return True

        # MACD阻力检查
        # if self.macdCheck(t, currentPrice):
        #     saveLog("macd阻力位, 禁止下单")
        #     return True

        if self.mvSyntony(currentPrice, t):
            saveLog("5日均线不在30分钟均线上/下方，禁止下单")
            return True

        if self.passRound(t, currentPrice) is False:
            # 目前只交易第一阻力
            # buyPrice, sellPrice, coreStrategy = self.articlePrice()
            # if coreStrategy == None or (coreStrategy == "sell"
            #                             and t == Order.CREATE_SELL) or (
            #                                 coreStrategy == "buy"
            #                                 and t == Order.CREATE_BUY):
            #     return self.laseOrderCheck(t, currentPrice, total)
            # else:
            #     saveLog("主策略方向和主信号方向不一致，禁止下单")
            #     return True
            return self.laseOrderCheck(t, currentPrice, total)
        else:
            saveLog("当前价格未在阻力区间内，禁止下单")
            return True

    def laseOrderCheck(self, t, currentPrice, total):
        """
        价格区间判断
        """
        if total is not None and len(total) != 0:
            maxBuy = 0
            minSell = 0

            for item in total:
                # 空单时获取最小开盘价
                if item.type == 1 and (minSell == 0
                                       or item.price_open < minSell):
                    minSell = item.price_open

                # 多单时获取最大开盘价
                if item.type == 0 and (maxBuy == 0
                                       or item.price_open > maxBuy):
                    maxBuy = item.price_open

            if t == Order.CREATE_BUY and currentPrice > maxBuy + self.goldPriceOffset:
                return False
            elif t == Order.CREATE_SELL and currentPrice < minSell - self.goldPriceOffset:
                return False
            else:
                saveLog("价格未到达目标位, 禁止下单")
                return True
        else:
            priceState = self.surpassPrice(t, currentPrice)
            if priceState is True:
                saveLog("当前价格未超过上一单目标价, 禁止下单")
            return priceState

    def passRound(self, t, currentPrice, level=1):
        """
        超出阻力区间禁止下单
        level  一级阻力
        leve2  最后一级阻力
        """
        levelPrice = []

        if len(strategyList) == 0:
            return True

        if "buy" in strategyList[0] and len(strategyList[0]["buy"]) != 0:
            buyStrategy = strategyList[0]["buy"]
            if "price" in buyStrategy and len(buyStrategy["price"]) != 0:
                levelPrice.extend(buyStrategy["price"])

            if "tp" in buyStrategy and len(buyStrategy["tp"]) != 0:
                levelPrice.extend(buyStrategy["tp"])

        if "sell" in strategyList[0] and len(strategyList[0]["sell"]) != 0:
            sellStrategy = strategyList[0]["sell"]
            if "price" in sellStrategy and len(sellStrategy["price"]) != 0:
                levelPrice.extend(sellStrategy["price"])

            if "tp" in sellStrategy and len(sellStrategy["tp"]) != 0:
                levelPrice.extend(sellStrategy["tp"])

        levelPrice = sorted(self.mergeNear(levelPrice, 2), reverse=True)

        if currentPrice < levelPrice[-1] or currentPrice > levelPrice[0]:
            # 1、当前价格在阻力外
            # 2、日内没有订单
            # 3、当前价格为新高/低
            historyOrder = self.dayAllOrder()
            if historyOrder is None or len(historyOrder) == 0:
                if t == Order.CREATE_SELL:
                    mDayLowPrice = self.getLowPriceArray(
                        self.symble, mt5.TIMEFRAME_D1)[-1]
                    if currentPrice <= mDayLowPrice:
                        saveLog("当前价格在阻力外，且日内没有订单成交，且当前价格为新低")
                        return False
                elif t == Order.CREATE_BUY:
                    mDayHighPrice = self.getHighPriceArray(
                        self.symble, mt5.TIMEFRAME_D1)[-1]
                    if currentPrice >= mDayHighPrice:
                        saveLog("当前价格在阻力外，且日内没有订单成交，且当前价格为新高")
                        return False
            return True

        if len(levelPrice) != 0:
            for index in range(0, len(levelPrice)):
                if currentPrice < levelPrice[
                        index] + self.goldPriceOffset and currentPrice > levelPrice[
                            index] - self.goldPriceOffset:
                    # 当前价格正好处于阻力位时禁止交易
                    return True
        return False

    def mergeNear(self, listValue, typeValue):
        """
        合并
        typeValue 1:空   2:多
        """

        if len(listValue) < 2:
            return listValue

        if typeValue == 1:
            listValue = sorted(listValue)
        elif typeValue == 2:
            listValue = sorted(listValue, reverse=True)

        for index in range(len(listValue) - 1, -1, -1):
            lastValue = listValue[index]
            for nextIndex in range(index - 1, -1, -1):
                nextValue = listValue[nextIndex]
                if abs(lastValue - nextValue) < 2:
                    listValue[index] = nextValue
                    lastValue = nextValue
        return list(set(listValue))

    def getStrategyTp(self, t):
        """
        获取文章策略的止盈位
        """
        try:
            currentPrice = self.control.getCurrentPrice(self.symble,
                                                        orderType=t)
            buyTpPointList, sellTpPointList = self.getLevelPrice()

            if t == Order.CREATE_BUY:
                if buyTpPointList is not None and len(buyTpPointList) != 0:
                    level1 = buyTpPointList[0]
                    level2 = buyTpPointList[1]
                    level3 = buyTpPointList[2]

                    if currentPrice < level1 - self.goldPriceOffset:
                        return level1 - 0.5
                    elif currentPrice < level2 - self.goldPriceOffset:
                        return level2 - 0.5
                    elif currentPrice < level3 - self.goldPriceOffset:
                        return level3 - 0.5
                return currentPrice + 4
            elif t == Order.CREATE_SELL:
                if sellTpPointList is not None and len(sellTpPointList) != 0:
                    level1 = sellTpPointList[2]
                    level2 = sellTpPointList[1]
                    level3 = sellTpPointList[0]

                    if currentPrice > level1 + self.goldPriceOffset:
                        return level1 + 0.5
                    elif currentPrice > level2 + self.goldPriceOffset:
                        return level2 + 0.5
                    elif currentPrice > level3 + self.goldPriceOffset:
                        return level3 + 0.5
                return currentPrice - 4
        except Exception as e:
            if t == Order.CREATE_BUY:
                return currentPrice + 4
            elif t == Order.CREATE_SELL:
                return currentPrice - 4

    def createRealOrder(self, orderType, orderPrice, remarkTag):
        """
        创建实时单
        """
        buyPrice = self.control.getCurrentPrice(self.symble,
                                                orderType=Order.CREATE_BUY)
        sellPrice = self.control.getCurrentPrice(self.symble,
                                                 orderType=Order.CREATE_SELL)

        if orderType == 1 and orderPrice - sellPrice > self.goldPriceOffset + 1:
            self.control.createOrder(OrderType.SELL,
                                     self.symble,
                                     priceSp=sellPrice + 3,
                                     remark=remarkTag)
        elif orderType == 0 and buyPrice - orderPrice > self.goldPriceOffset + 1:
            self.control.createOrder(OrderType.BUY,
                                     self.symble,
                                     priceSp=buyPrice - 3,
                                     remark=remarkTag)

    def getLevelPrice(self):
        """
        获取策略阻力位
        """

        buyTpPointList = None
        sellTpPointList = None

        if len(strategyList) != 0:
            if "buy" in strategyList[0] and len(strategyList[0]["buy"]) != 0:
                buyStrategy = strategyList[0]["buy"]
                if "price" in buyStrategy and len(buyStrategy["price"]) != 0:
                    buyPointList = buyStrategy["price"]
                    buyTpPointList = buyStrategy["tp"]

            if "sell" in strategyList[0] and len(strategyList[0]["sell"]) != 0:
                sellStrategy = strategyList[0]["sell"]
                if "price" in sellStrategy and len(sellStrategy["price"]) != 0:
                    sellPointList = sellStrategy["price"]
                    sellTpPointList = sellStrategy["tp"]
        return buyTpPointList, sellTpPointList

    def passZone(self):
        """
        区间内禁止下单
        当前没有持仓订单时, 判断前两单是否为一个多单一个空单
        获取前订单最高最低价进行区间限定
        """
        total = mt5.positions_get()
        if total is None or len(total) == 0:
            historyOrder = self.dayAllOrder()
            if historyOrder is not None and len(historyOrder) >= 2:
                latest = historyOrder[-1]
                last = historyOrder[-2]

                if "profit" in latest and latest[
                        "profit"] <= 0 and "profit" in last and last[
                            "profit"] <= 0:
                    top = 0
                    bottom = 0
                    if latest["type"] == 0 and last["type"] == 1:
                        top = latest["open_price"]
                        bottom = last["open_price"]
                    elif latest["type"] == 1 and last["type"] == 0:
                        top = last["open_price"]
                        bottom = latest["open_price"]

                    currentPrice = self.control.getCurrentPrice(self.symble)
                    if currentPrice > top + self.goldPriceOffset or currentPrice < bottom - self.goldPriceOffset:
                        return True
                    else:
                        saveLog("当前价格处于阻力区间之内，禁止交易")
                        return False
        return True

    def surpassPrice(self, t, currentPrice):
        """
        当前不存在订单时
        当前价格超过上一单止盈价
        """
        historyOrder = self.dayAllOrder()
        if historyOrder is not None and len(historyOrder) != 0:
            lastOrder = historyOrder[-1]

            if "close_price" in lastOrder and "open_price" in lastOrder and "profit" in lastOrder and "type" in lastOrder:
                closePrice = lastOrder["close_price"]
                openPrice = lastOrder["open_price"]
                orderProfit = lastOrder["profit"]
                orderType = lastOrder["type"]

                if orderType == 1:  # 上一单位空单
                    if t == Order.CREATE_BUY:
                        return False

                    if orderProfit > 0 and currentPrice < closePrice - self.goldPriceOffset:
                        return False
                    elif orderProfit <= 0 and currentPrice < openPrice - self.goldPriceOffset:
                        return False
                    else:
                        return True
                else:  # 上一单为多单
                    if t == Order.CREATE_SELL:
                        return False

                    if orderProfit > 0 and currentPrice > closePrice + self.goldPriceOffset:
                        return False
                    elif orderProfit <= 0 and currentPrice > openPrice + self.goldPriceOffset:
                        return False
                    else:
                        return True
        return False

    def articlePrice(self):
        """
        获取策略价格
        """
        buyCache = 0
        sellCache = 0
        coreStrategy = None

        if len(strategyList) != 0:
            currentPrice = self.control.getCurrentPrice(self.symble)
            if "buy" in strategyList[0] and len(strategyList[0]["buy"]) != 0:
                buyStrategy = strategyList[0]["buy"]
                if "price" in buyStrategy and len(buyStrategy["price"]) != 0:
                    buyCache = buyStrategy["price"][-1]

            if "sell" in strategyList[0] and len(strategyList[0]["sell"]) != 0:
                sellStrategy = strategyList[0]["sell"]
                if "price" in sellStrategy and len(sellStrategy["price"]) != 0:
                    sellCache = sellStrategy["price"][0]

            if "main" in strategyList[0]:
                coreStrategy = strategyList[0]["main"]
        return buyCache, sellCache, coreStrategy

    def backOrderLevel(self, orderType):
        """
        回调单阻力位，找最近的一根均线阻力位
        orderType  1:空单     0:多单
        """
        maList = self.shortFrame()
        if orderType == 1:
            currentPrice = self.control.getCurrentPrice(
                self.symble, orderType=Order.CREATE_SELL)

            nearPrice = self.findNumber(maList, currentPrice, 1)
            if nearPrice != None and currentPrice - nearPrice < 0.15:
                return True
        else:
            currentPrice = self.control.getCurrentPrice(
                self.symble, orderType=Order.CREATE_BUY)

            nearPrice = self.findNumber(maList, currentPrice, 0)
            if nearPrice != None and nearPrice - currentPrice < 0.15:
                return True
        return False

    def findNumber(self, numbers, target, direction):
        smallerOrBigger = None
        for number in numbers:
            if direction == 1:
                if number < target and (smallerOrBigger is None or target -
                                        number < target - smallerOrBigger):
                    smallerOrBigger = number
            else:
                if number > target and (smallerOrBigger is None or number -
                                        target < smallerOrBigger - target):
                    smallerOrBigger = number
        return smallerOrBigger

    def importantLevel(self):
        """
        当前价格所处的阻力位
        return 价格、趋势、level
        """
        buyPrice = None
        sellPrice = None
        level = None
        trend = None

        if len(strategyList) != 0:
            currentPrice = self.control.getCurrentPrice(self.symble)
            if "buy" in strategyList[0] and len(strategyList[0]["buy"]) != 0:
                buyStrategy = strategyList[0]["buy"]
                if "price" in buyStrategy and len(buyStrategy["price"]) != 0:
                    for index in range(len(buyStrategy["price"])):
                        levelBuyPrice = buyStrategy["price"][index]
                        if currentPrice < levelBuyPrice + self.goldPriceOffset and currentPrice > levelBuyPrice - self.goldPriceOffset:
                            level = index
                            trend = 1
                            buyPrice = buyStrategy["price"][index]
                            break

            if "sell" in strategyList[0] and len(strategyList[0]["sell"]) != 0:
                sellStrategy = strategyList[0]["sell"]
                if "price" in sellStrategy and len(sellStrategy["price"]) != 0:
                    for index in range(len(sellStrategy["price"])):
                        levelSellPrice = sellStrategy["price"][index]
                        if currentPrice > levelSellPrice - self.goldPriceOffset and currentPrice < levelSellPrice + self.goldPriceOffset:
                            level = index
                            trend = 2
                            sellPrice = sellStrategy["price"][index]
                            break
        if (buyPrice != None and sellPrice != None) or (buyPrice == None
                                                        and sellPrice == None):
            return None, None, None
        return (buyPrice if buyPrice != None else sellPrice), trend, level

    # def dealArticleStrategy(self):
    #     """
    #     文章策略处理
    #     """
    #     nowTime = datetime.now()
    #     localStart = datetime(nowTime.year, nowTime.month, nowTime.day, 0)

    #     gmtStart = local_2_gmt(datetime2Str(localStart))
    #     gmtDateTime = str2datetime(gmtStart)
    #     allOrder = [
    #         item for item in mt5.history_deals_get(gmtDateTime, nowTime)
    #         if item.entry == 1
    #     ]
    #     # 1:多  0:空
    #     print("aaa")

    def fibFilter(self, currentPrice):
        """
        斐波那契过滤
        """

        for item in fibList:
            if currentPrice < item + self.goldPriceOffset and currentPrice > item - self.goldPriceOffset:
                return True

        for item in fibList2:
            if currentPrice < item + self.goldPriceOffset and currentPrice > item - self.goldPriceOffset:
                return True

        for item in fibList3:
            if currentPrice < item + self.goldPriceOffset and currentPrice > item - self.goldPriceOffset:
                return True
        return False

    def mvSyntony(self, currentPrice, t):
        """
        5日均线穿插状态
        return False:不在阻力位    True:在阻力位
        """
        closeArr = []
        result = self.getKLine(self.symble, 100, mt5.TIMEFRAME_M15,
                               start=0)["value"]
        for item in result:
            closeArr.append(item[4])

        ma30Value = self.indicator.latestMaValue(closeArr, 30)
        ma20Value = self.indicator.latestMaValue(closeArr, 20)
        ma5Value = self.indicator.latestMaValue(closeArr, 5)

        if t == Order.CREATE_BUY and ma5Value > max(
                ma30Value, ma20Value) + 0.5 and currentPrice > ma5Value:
            return False
        if t == Order.CREATE_SELL and ma5Value < min(
                ma30Value, ma20Value) - 0.5 and currentPrice < ma5Value:
            return False
        return True

    def checkMvValue(self, mvMap, parentKey, currentPrice):
        """
        检查当前价格是否在移动平均线附近
        """

        if mvMap is None and mvMap != "":
            return True

        if currentPrice > mvMap["200"] - (
                self.goldPriceOffset + 1) and currentPrice < mvMap["200"] + (
                    self.goldPriceOffset + 1):
            saveLog("当前价格处于" + parentKey + "周期的200日均线阻力位，禁止交易")
            return False
        elif currentPrice > mvMap["100"] - (
                self.goldPriceOffset + 1) and currentPrice < mvMap["100"] + (
                    self.goldPriceOffset + 1):
            saveLog("当前价格处于" + parentKey + "周期的100日均线阻力位，禁止交易")
            return False
        elif currentPrice > mvMap["50"] - (
                self.goldPriceOffset +
                1) and currentPrice < mvMap["50"] + (self.goldPriceOffset + 1):
            saveLog("当前价格处于" + parentKey + "周期的50日均线阻力位，禁止交易")
            return False
        elif currentPrice > mvMap["20"] - (
                self.goldPriceOffset +
                1) and currentPrice < mvMap["20"] + (self.goldPriceOffset + 1):
            saveLog("当前价格处于" + parentKey + "周期的20日均线阻力位，禁止交易")
            return False
        else:
            return True
